using System.IO;
using System.Runtime.Serialization;
using System.Text;
using Sustainalytics.Alerts.Entities;
using Sustainalytics.ClientDW.Entities.Alerts;
using Sustainalytics.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using Sustainalytics.ClientDW.Entities;

namespace Sustainalytics.Events.DataAccessLayer
{
    public class EventsDataReader : IEventsDataReader
    {
        private readonly EventsDatabase _database;

        protected EventsDataReader(EventsDatabase database)
        {
            _database = database;
        }

        public static IEventsDataReader GetInstance(string connectionString, int commandTimeout)
        {
            if (string.IsNullOrEmpty(connectionString))
                throw new ArgumentException("A connection string must be provided to create an CorporateDataReader instance!");

            if (commandTimeout == -1)
            {
                commandTimeout = SyncHelper.ReadCommandTimeOutFromConfig();
            }

            return new EventsDataReader(new EventsDatabase(connectionString, commandTimeout));
        }

        public virtual List<Alert> ListClientAlerts(DateTime createdAfter)
        {
            var alerts = new List<Alert>();

            using (var reader = _database.Alerts(createdAfter))
            {
                if (reader == null) return alerts;

                while (reader.Read())
                {
                    var alert = new Alert();

                    alert.Id = (Guid)reader["Id"];
                    alert.CompanyId = (Guid)reader["CompanyId"];
                    alert.LevelOfImportance = (int)reader["LevelOfImportance"];
                    alert.AlertType = (AlertChangeType)(int)reader["AlertType"];
                    alert.TypeOfChange = (TypeOfChange)(int)reader["TypeOfChange"];
                    alert.Rationale = (string)reader["Rationale"];
                    alert.Data = new DataContractSerializer(typeof (AlertData)).ReadObject(
                        new MemoryStream(Encoding.UTF8.GetBytes((string)reader["ResourceDataSerialized"]))) as AlertData;
                    alert.Created = (DateTime)reader["Created"];
                    alert.LastUpdated = (DateTime)reader["LastUpdated"];

                    alerts.Add(alert);
                }
            }

            return alerts;
        }

        public List<EventIndicatorCompanyProfile> GetPublishedCompanyProfiles()
        {
            var companyProfiles = new List<EventIndicatorCompanyProfile>();

            using (var reader = _database.CompanyProfiles())
            {
                if (reader == null) return companyProfiles;

                while (reader.Read())
                {
                    var companyProfile = new EventIndicatorCompanyProfile();

                    companyProfile.CompanyId = (Guid)reader["CompanyId"];
                    companyProfile.LastUpdated = (DateTime)reader["LastUpdate"];

                    companyProfiles.Add(companyProfile);
                }
            }

            return companyProfiles;
        }

        public List<BaseEventIndicator> ListBaseEventIndicators()
        {
            var baseEventIndicators = new List<BaseEventIndicator>();

            using (var reader = _database.EventIndicatorTemplates())
            {
                if (reader == null) return baseEventIndicators;

                while (reader.Read())
                {
                    var newsArticle = new BaseEventIndicator();

                    newsArticle.Id = (Guid)reader["Id"];
                    newsArticle.Name = (string)reader["Name"];
                    newsArticle.Number = (string)reader["Number"];

                    baseEventIndicators.Add(newsArticle);
                }
            }

            return baseEventIndicators;
        }

        public List<Incident> GetIncidents(List<Guid> companyIds)
        {
            var incidents = new List<Incident>();

            using (var reader = _database.Incidents(companyIds))
            {
                if (reader == null) return incidents;

                while (reader.Read())
                {
                    incidents.Add(new Incident
                    {
                        Id = (Guid)reader["Id"],
                        CompanyId = (Guid)reader["CompanyId"],
                        EventIndicatorId = (Guid)reader["EventIndicatorId"],
                        IncidentId = (string)reader["IncidentId"],
                        Type = (IncidentType)(int)reader["Type"],
                        Name = (string)reader["Name"],
                        Summary = (string)reader["Summary"],
                        Analysis = (string)reader["Analysis"],
                        MainTag = (string)reader["MainTag"],
                        Source = (string)reader["Source"],
                        IncidentDate = (DateTime)reader["IncidentDate"],
                        Location = new Location
                        {
                            Address = (string)reader["LocationAddress"],
                            Latitude = (double)reader["LocationLatitude"],
                            Longitude = (double)reader["LocationLongitude"]
                        },
                        ImpactScore = (double)reader["ImpactScore"],
                        RiskScore = (double)reader["RiskScore"],
                        HasSupplyChainSpecialTag = Convert.ToBoolean(reader["HasSupplyChainSpecialTag"]),
                        LastUpdated = (DateTime)reader["LastUpdated"],                        
                        
                        EventIndicatorName = (string)reader["EventIndicatorName"], 
                        ExternalUrl = reader["ExternalUrl"] as string,

                        IsMajorIncident = (bool)reader["IsMajorIncidentInChain"],
                        ImpactTrend = (int)reader["ImpactTrendInChain"],
                        RiskTrend = (int)reader["RiskTrendInChain"]
                    });
                }
            }

            return incidents;
        }

        private Dictionary<Guid, List<string>> GetEventIndicatorComments(List<Guid> companyIds)
        {
            var eventIndicatorComments = new Dictionary<Guid, List<string>>();

            using (var reader = _database.EventIndicatorComments(companyIds))
            {
                if (reader == null) return eventIndicatorComments;

                var currentCommentId = Guid.Empty;
                var currentCommentTexts = new List<string>();

                while (reader.Read())
                {
                    var commentId = (Guid)reader["CommentId"];

                    if (currentCommentId != commentId)
                    {
                        currentCommentId = commentId;

                        currentCommentTexts = new List<string>();

                        eventIndicatorComments.Add(currentCommentId, currentCommentTexts);
                    }

                    currentCommentTexts.Add((string)reader["Comment"]);
                }
            }

            return eventIndicatorComments;
        }

        public List<EventIndicator> GetEventIndicators(List<Guid> companyIds)
        {
            var eventIndicatorComments = GetEventIndicatorComments(companyIds);
            var eventIndicators = new List<EventIndicator>();

            using (var reader = _database.EventIndicators(companyIds))
            {
                if (reader == null) return eventIndicators;

                while (reader.Read())
                {
                    var impactCommentsId = reader["ImpactCommentsId"] as Guid?;
                    var riskCommentsId = reader["RiskCommentsId"] as Guid?;
                    var preparednessCommentsId = reader["PreparednessCommentsId"] as Guid?;
                    var outlookCommentsId = reader["OutlookCommentsId"] as Guid?;

                    eventIndicators.Add(new EventIndicator
                    {
                        Id = (Guid)reader["EventIndicatorId"],
                        Name = (string)reader[EventIndicatorSqlFields.EventIndicatorName],
                        BaseEventIndicatorId = (Guid)reader["BaseEventIndicatorId"],

                        CompanyId = (Guid)reader[EventIndicatorSqlFields.CompanyId],

                        ControversyIndicatorId = (Guid)reader["ControversyIndicatorId"],
                        ControversyIndicatorName = (string)reader["ControversyIndicatorName"],
                        ControversyIndicatorNumber = (string)reader["ControversyIndicatorNumber"],
                        ControversyIndicatorSortKey = (int)reader["ControversyIndicatorSortKey"],
                        ProvidesControversy = (bool)reader["ProvidesControversy"],
                        ControversySummary = reader["ControversySummary"] as string,

                        AnswerCategory = (int)reader[EventIndicatorSqlFields.AnswerCategoryId],
                        AnswerCategoryText = (string)reader["AnswerCategoryText"],
                        AnswerCategoryScore = (int)reader["AnswerCategoryScore"],
                        AnswerCategoryJustification = reader["AnswerCategoryJustification"] as string,

                        ImpactScore = (double)reader["ImpactScore"],
                        ImpactText = reader["ImpactText"] as string,
                        ImpactComments = impactCommentsId.HasValue ? eventIndicatorComments.TryGetValue(impactCommentsId.Value, out var ic) ? ic : new List<string>() 
                                                                    : null,

                        RiskScore = (double)reader["RiskScore"],
                        RiskText = reader["RiskText"] as string,
                        RiskComments = riskCommentsId.HasValue ? eventIndicatorComments.TryGetValue(riskCommentsId.Value, out var rc) ? rc : new List<string>() 
                                                                : null,

                        PreparednessScore = reader["PreparednessScore"] as int?,
                        PreparednessText = reader["PreparednessText"] as string,
                        PreparednessComments = preparednessCommentsId.HasValue ? eventIndicatorComments.TryGetValue(preparednessCommentsId.Value, out var pc) ? pc : new List<string>()
                                                                                : null,

                        OutlookScore = reader["OutlookScore"] as int?,
                        OutlookText = reader["OutlookText"] as string,
                        OutlookComments = outlookCommentsId.HasValue ? eventIndicatorComments.TryGetValue(outlookCommentsId.Value, out var oc) ? oc : new List<string>()
                                                                        : null
                    });
                }
            }            

            return eventIndicators;
        }

        public List<NewsArticle> GetNewsArticles()
        {
            var newsArticles = new List<NewsArticle>();

            using (var reader = _database.NewsArticles())
            {
                if (reader == null) return newsArticles;

                while (reader.Read())
                {
                    var newsArticle = new NewsArticle();

                    newsArticle.Id = (Guid)reader["Id"];
                    newsArticle.CompanyId = (Guid)reader["CompanyId"];
                    newsArticle.PublicationDate = (DateTime)reader["PublicationDate"];
                    newsArticle.Headline = (string)reader["Headline"];
                    newsArticle.ExternalUrl = (string)reader["ExternalUrl"];
                    newsArticle.Source = (string)reader["Source"];

                    newsArticles.Add(newsArticle);
                }
            }

            return newsArticles;
        }
    }
}
